package tools.illumina;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;

//import tools.fastq.FastqParser_nio;
import tools.fastq.FastqSeq;
import tools.fastq.fastqParser;

public class illuminaUtils {
	final static String sep="\t";

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		if(args.length>0){
			if(args[0].equals("newblerHeader")&&args.length>3){
				newblerHeader(args[1],args[2],args[3]);
			}else if(args[0].equals("analyzeClonality")&&args.length>5){
				ArrayList<String> files= new ArrayList<String>();
				for(int i=5;i<args.length;i++){
					files.add(args[i]);
				}
				analyzeClonality(files,Integer.parseInt(args[1]),Integer.parseInt(args[2]),args[3],args[4]);
			}else if(args[0].equals("combineClonalityPairs")&&args.length>4){
				combineClonalityPairs(args[1],args[2],Integer.parseInt(args[3]),Integer.parseInt(args[4]));
			}else if(args[0].equals("filterClonalityPairs")&&args.length==3){
				filterClonalityPairs(args[1],args[2]);
			}else if(args[0].equals("filterClonalitySinglets")&&args.length==3){
				filterClonalitySinglets(args[1],args[2],args[3],1);
			}else if(args[0].equals("filterClonalitySinglets")&&args.length>3){
				filterClonalitySinglets(args[1],args[2],args[3],Integer.parseInt(args[4]));
			}else if(args[0].equals("tabulatedTopOnly")&&args.length==2){
//				tabulatedTopOnly(args[1]);
			}else{
				System.err.println(printHelp());
				System.exit(616);
			}
		}else{
			System.err.println(printHelp());
			System.exit(616);
		}
	}

	private static String printHelp(){
		String help="Usage: illuminaUtils <cmd> <input>\n";
		help+="where <cmd> is:\n";
		help+="newblerHeader - Takes one of the fastq files from a paired run and generates a fasta and qual file with newbler headers\n";
		help+="\t<input> = <fastq file> <library name> <outprefix>\n";
		help+="analyzeClonality - (step 01, mem) takes multiple trimmed fastq file, counts the N and identifies clonality reads. The radix will split the file into buckets if larger than 0\n";
		help+="\t<input> = <min length> <radix> <outfile> <tmpPrefix> <fastqFile 1> ... <fastqFile N>\n";
		help+="combineClonalityPairs - (step 02, stdout) takes two clonality files and combines them into a single file\n";
		help+="\t<input> = <clonality_1 file> <clonality_2 file> <min length> <max N count>\n";
		help+="filterClonalityPairs - (step 03, mem) takes a merged clonality file and outputs the longest pair for each cluster pair\n";
		help+="\t<input> = <clonality merge file> <outprefix>\n"; // <max to return=1>\n";
		help+="filterClonalitySinglets - (step 04, mem) takes a clonality file and a file with singlet ids The output consist of the longest singlet from each clonality cluster.\n";
		help+="\t<input> = <clonality file> <singlet ids file> <good ids file> <max to return=1>\n";
			
		return help;
	}
	
	public static void filterClonalitySinglets(String clonalityFile, String singletIdFile,String goodIdFile,int max)throws Exception{
		HashSet<String> singlets= new HashSet<String>(30000000);
		HashSet<String> good= new HashSet<String>(30000000);
		HashSet<String> clustersToParse= new HashSet<String>();
		HashMap<String, HashMap<String, ArrayList<filteredPairs_item>>> clusters= new HashMap<String, HashMap<String,ArrayList<filteredPairs_item>>>();
		HashMap<String, ArrayList<filteredPairs_item>> cl;
		ArrayList<filteredPairs_item> tmpAL;
		
		filteredPairs_item fpi;
		
		BufferedReader in= new BufferedReader(new FileReader(singletIdFile));
		
		System.err.println("Read singlets...");
		int linenr=1;
		for(String s=in.readLine();s!=null;s=in.readLine(),linenr++){
			if(linenr%100000==0){
				System.err.print("\t"+linenr+"\r");
			}
			singlets.add(s);
		}
		System.err.println("Read the good ids...");
		linenr=1;
		in= new BufferedReader(new FileReader(goodIdFile));
		for(String s=in.readLine();s!=null;s=in.readLine(),linenr++){
			if(linenr%100000==0){
				System.err.print("\t"+linenr+"\r");
			}
			good.add(s);
		}
		
		System.err.println("Read clonality file...");
		in= new BufferedReader(new FileReader(clonalityFile));
		linenr=1;
		for(String s=in.readLine();s!=null;s=in.readLine(),linenr++){
			if(linenr%10000==0){
				System.err.print("\t"+linenr+"\r");
			}
			final String[] l= s.split("\t");
			boolean two=false;
			if(singlets.contains(l[0])){
				two=true;
				fpi= new filteredPairs_item(l[0], Integer.parseInt(l[1]), Integer.parseInt(l[3]));
				if(clusters.containsKey(l[3])){
					cl=clusters.get(l[3]);
					if(cl.containsKey("s")){
						//compare
						tmpAL=cl.get("s");
						tmpAL.add(fpi);
						Collections.sort(tmpAL);
						while(tmpAL.size()>max){
							tmpAL.remove(max);
						}
					}else{
						//just add
						tmpAL= new ArrayList<filteredPairs_item>();
						tmpAL.add(fpi);
						cl.put("s", tmpAL);
						clustersToParse.add(l[3]);
					}
				}else{
					//just insert
					cl= new HashMap<String, ArrayList<filteredPairs_item>>();
					tmpAL= new ArrayList<filteredPairs_item>();
					tmpAL.add(fpi);
					cl.put("s", tmpAL);
					clusters.put(l[3], cl);
					clustersToParse.add(l[3]);
				}
			}
			if(good.contains(l[0])){
				if(two){
					System.err.println("both singlet and pair: "+l[0]);
				}
				fpi= new filteredPairs_item(l[0], Integer.parseInt(l[1]), Integer.parseInt(l[3]));
				if(clusters.containsKey(l[3])){
					cl=clusters.get(l[3]);
					if(cl.containsKey("g")){
						tmpAL=cl.get("g");
						if(fpi.getLength1()>tmpAL.get(0).getLength1()){
							tmpAL.remove(0);
							tmpAL.add(fpi);
						}
					}else{
						//just add
						tmpAL=new ArrayList<filteredPairs_item>();
						tmpAL.add(fpi);
						cl.put("g", tmpAL);
					}
				}else{
					//insert
					cl= new HashMap<String, ArrayList<filteredPairs_item>>();
					tmpAL=new ArrayList<filteredPairs_item>();
					tmpAL.add(fpi);
					cl.put("g", tmpAL);
					clusters.put(l[3], cl);
				}
			}
		}
		System.err.println("Printing...");
		BufferedWriter out= new BufferedWriter(new FileWriter(singletIdFile+".good"));
		for (String cluster : clustersToParse) {
			cl= clusters.get(cluster);
			tmpAL= cl.get("s");
			if(cl.containsKey("g")){
				fpi=cl.get("g").get(0);
				for (filteredPairs_item fpi_out : tmpAL) {
					if(fpi_out.getLength1()>fpi.getLength1()){
						out.write(fpi_out.getName1()+"\n");
					}
				}
			}else{
				//print
				for (filteredPairs_item fpi_out : tmpAL) {
					out.write(fpi_out.getName1()+"\n");
				}
				
			}
		}
		out.close();
		System.err.println("Done!");
	}
	
	public static void filterClonalityPairs(String mergeFile, String outprefix)throws Exception{
		BufferedReader in= new BufferedReader(new FileReader(mergeFile));
		HashMap<String, filteredPairs_item> items= new HashMap<String, filteredPairs_item>();
//		ArrayList<filteredPairs_item> tmpAL;
		
		HashSet<String> bad1= new HashSet<String>(), bad2=new HashSet<String>();
		HashSet<String> good1= new HashSet<String>(), good2=new HashSet<String>();
		
		filteredPairs_item fpi;
		
		System.err.println("Reading file...");
		int linenr=1;
		
		for(String s= in.readLine();s!=null;s=in.readLine(),linenr++){
			if(linenr%10000==0){
				System.err.print("\t"+linenr+"\r");
			}
			final String[] l= s.split("\t");
			fpi= new filteredPairs_item(l[0]+"/1", l[0]+"/2", Integer.parseInt(l[1]), Integer.parseInt(l[2]), Integer.parseInt(l[3]));
//			if(items.containsKey(l[3])){
//				tmpAL=items.get(l[3]);
//				Collections.sort(tmpAL);
//				while(tmpAL.size()>max){
//					fpi=tmpAL.remove(max);
//					bad1.add(fpi.getName1());
//					bad2.add(fpi.getName2());
//				}
//			}else{
//				tmpAL= new ArrayList<filteredPairs_item>();
//				tmpAL.add(fpi);
//				items.put(l[3], tmpAL);
//			}				
				
			if(items.containsKey(l[3])){
				//check if this is longer
				if(fpi.getTotalLength()>items.get(l[3]).getTotalLength()){
					//update, if so
					fpi=items.put(l[3], fpi);
					if(fpi!=null){
						bad1.add(fpi.getName1());
						bad2.add(fpi.getName2());
					}
				}else{
					bad1.add(l[0]+"/1");
					bad2.add(l[0]+"/2");
				}
			}else{
				items.put(l[3], fpi);
			}

		}
		
		System.err.println("Printing good pairs...");
		BufferedWriter out1= new BufferedWriter(new FileWriter(outprefix+"_1.good"));
		BufferedWriter out2= new BufferedWriter(new FileWriter(outprefix+"_2.good"));
		for (filteredPairs_item fpi_out : items.values()) {
			if(good1.add(fpi_out.getName1())){
				out1.write(fpi_out.getName1()+"\n");
				bad1.remove(fpi_out.getName1());
			}
			if(good2.add(fpi_out.getName2())){
				out2.write(fpi_out.getName2()+"\n");
				bad2.remove(fpi_out.getName2());
			}
		}
//		for (ArrayList<filteredPairs_item> list : items.values()) {
//			for (filteredPairs_item fpi_out : list) {
//				if(good1.add(fpi_out.getName1())){
//					out1.write(fpi_out.getName1()+"\n");
//					bad1.remove(fpi_out.getName1());
//				}
//				if(good2.add(fpi_out.getName2())){
//					out2.write(fpi_out.getName2()+"\n");
//					bad2.remove(fpi_out.getName2());
//				}
//			}
//		}
		out1.close();
		out2.close();
		System.err.println("Printing 1.bad...");
		out1= new BufferedWriter(new FileWriter(outprefix+"_1.bad"));
		for (String name : bad1) {
			out1.write(name+"\n");
		}
		out1.close();
		System.err.println("Printing 2.bad...");
		out1= new BufferedWriter(new FileWriter(outprefix+"_2.bad"));
		for (String name : bad2) {
			out1.write(name+"\n");
		}
		out1.close();
		System.err.println("Done!");
	}
	
//	public static void combineClonalityPairs_old(String clonality1File,String clonality2File,int minLength, int maxNcount)throws Exception{
//		
//		BufferedReader in=new BufferedReader(new FileReader(clonality1File));
//		HashMap<String, filteredPairs_item> items= new HashMap<String, filteredPairs_item>();
////		HashMap<Integer, HashSet<String>> clusters= new HashMap<Integer, HashSet<String>>();
//		
//		filteredPairs_item fpi;
//		
//		System.err.println("Reading first file...");
//		int lineNr=1;
//		for(String s=in.readLine();s!=null;s=in.readLine(),lineNr++){
//			if(lineNr%1000000==0){
//				System.err.println(lineNr);
//			}
//			final String[] l=s.split("\t");
//			if(Integer.parseInt(l[2])<=maxNcount){
//				final int length=Integer.parseInt(l[1]);
//				if(length>=minLength){
//					//add
//					final int cluster=Integer.parseInt(l[3]);
//					fpi=new filteredPairs_item(l[0], length, cluster);
////					if(!clusters.containsKey(cluster)){
////						clusters.put(cluster, new HashSet<String>());
////					}
////					clusters.get(clusters).add(fpi.getPair());
//					if(items.containsKey(fpi.getPair())){
//						items.get(fpi.getPair()).addCluster1(cluster);
//					}else{
//						items.put(fpi.getPair(), fpi);
//					}
//				}
//			}
//		}
//		//parse the second file
//		lineNr=1;
//		in=new BufferedReader(new FileReader(clonality2File));
//		HashMap<filteredPairs_pair, Integer>pairIds = new HashMap<filteredPairs_pair, Integer>();
//		filteredPairs_pair fpp;
//		int pairNr=0;
//		HashSet<String> singlets2= new HashSet<String>();
//		for(String s= in.readLine();s!=null;s=in.readLine(),lineNr++){
//			if(lineNr%1000000==0){
//				System.err.println(lineNr);
//			}
//			final String[] l= s.split("\t");
//			if(Integer.parseInt(l[2])<=maxNcount){
//				final int length=Integer.parseInt(l[1]);
//				if(length>=minLength){
//					//use
//					final int cluster=Integer.parseInt(l[3]);
//					final String pair=l[0].substring(0, l[0].length()-2);
//					if(items.containsKey(pair)){
//						fpi=items.get(pair);
//						//print pair
//						final String line= fpi.getPair()+sep+fpi.getName1()+sep+l[0]+sep+fpi.getLength1()+sep+length+sep;
//						for (Integer cluster1 : fpi.getClusters1()) {
//							fpp=new filteredPairs_pair(cluster1,cluster);
//							if(!pairIds.containsKey(fpp)){
//								pairNr++;
//								pairIds.put(fpp, pairNr);
//							}
//							System.out.println(line+pairIds.get(fpp));
//						}
//						fpi.addMate();
//					}else{
//						//add singlet
//						singlets2.add(l[0]);
//					}
//				}
//			}
//		}
//		
//		System.err.println("Printing singlets...");
//		BufferedWriter out= new BufferedWriter(new FileWriter(clonality1File+".singlets"));
//		for (filteredPairs_item fpi_out : items.values()) {
//			if(fpi_out.getSinglet()){
//				out.write(fpi_out.getName1()+"\n");
//			}
//		}
//		out.close();
//		out=new BufferedWriter(new FileWriter(clonality2File+".singlets"));
//		for (String id : singlets2) {
//			out.write(id+"\n");
//		}
//		out.close();
//		System.err.println("Done!");
//	}
	
	public static void combineClonalityPairs(String clonality1File,String clonality2File,int minLength, int maxNcount)throws Exception{
		BufferedReader cf1= new BufferedReader(new FileReader(clonality1File));
		BufferedReader cf2= new BufferedReader(new FileReader(clonality2File));
		
		BufferedWriter singlets1= new BufferedWriter(new FileWriter(clonality1File+".singlets"));
		BufferedWriter singlets2= new BufferedWriter(new FileWriter(clonality2File+".singlets"));
		
		filteredPairs_item fpi1= getItemFromClonalityFile(cf1, minLength, maxNcount);
		filteredPairs_item fpi2= getItemFromClonalityFile(cf2, minLength, maxNcount);
		filteredPairs_item nextfpi1=null,nextfpi2=null;
		
		System.err.println("Reading both files:");
		
		int f1=0,f2=0,pairNr=0;
		HashMap<filteredPairs_pair, Integer> pairs= new HashMap<filteredPairs_pair, Integer>();
		filteredPairs_pair fpp;
		String rowStart;
		
		while(fpi1!=null&&fpi2!=null){
			if(f1%10000==0 || f2%10000==0){
				System.err.print("\t"+f1+"/"+f2+"\r");
			}
			if(nextfpi1==null){
				nextfpi1=getItemFromClonalityFile(cf1, minLength, maxNcount);
				f1++;
				if(nextfpi1==null){
					nextfpi1= new filteredPairs_item("", -1, -1);
				}
				if(fpi1.getName1().equals(nextfpi1.getName1())){
					fpi1.addCluster1(nextfpi1.getOneCluster());
					nextfpi1=null;
				}
			}
			if(nextfpi2==null){
				nextfpi2=getItemFromClonalityFile(cf2, minLength, maxNcount);
				f2++;
				if(nextfpi2==null){
					nextfpi2= new filteredPairs_item("",-1,-1);
				}else if(fpi2.getName1().equals(nextfpi2.getName1())){
					fpi2.addCluster1(nextfpi2.getOneCluster());
					nextfpi2=null;
				}
			}
			if(nextfpi1!=null && nextfpi2!=null){
				if(fpi1.getPair().equals(fpi2.getPair())){
					//print pair
					rowStart=fpi1.getPair()+sep+fpi1.getLength1()+sep+fpi2.getLength1()+sep;
					for (Integer c1 : fpi1.getClusters1()) {
						for (Integer c2 : fpi2.getClusters1()) {
							fpp= new filteredPairs_pair(c1,c2);
							if(!pairs.containsKey(fpp)){
								pairs.put(fpp, pairNr++);
							}
							System.out.println(rowStart+pairs.get(fpp));
						}
					}
					//reload both
					if(nextfpi1.getLength1()==-1){
						fpi1=null;
					}else{
						fpi1=new filteredPairs_item(nextfpi1);
						nextfpi1=null;
					}
					if(nextfpi2.getLength1()==-1){
						fpi2=null;
					}else{
						fpi2=new filteredPairs_item(nextfpi2);
						nextfpi2=null;
					}
				}else if(fpi1.getPair().compareTo(fpi2.getPair())<0){
					//print fpi1 as singlet and reload
					singlets1.write(fpi1.getName1()+"\n");
					if(nextfpi1.getLength1()==-1){
						fpi1=null;
					}else{
						fpi1=new filteredPairs_item(nextfpi1);
						nextfpi1=null;
					}
				}else if(fpi1.getPair().compareTo(fpi2.getPair())>0){
					//print fpi2 as singlet and reload
					singlets2.write(fpi2.getName1()+"\n");
					if(nextfpi2.getLength1()==-1){
						fpi2=null;
					}else{
						fpi2=new filteredPairs_item(nextfpi2);
						nextfpi2=null;
					}
				}else{
					System.err.println("strange combination: \t"+fpi1.getName1()+"\t"+fpi2.getName1());
				}
			}
		}
		String last="";
		if(fpi1!=null){
			//print remaining as singlets
			System.err.println("printing the remenenants of file 1");
			last=fpi1.getName1();
			singlets1.write(last+"\n");
			if(nextfpi1==null){
				fpi1= getItemFromClonalityFile(cf1, minLength, maxNcount);
			}else{
				fpi1=new filteredPairs_item(nextfpi1);
			}
			for(;fpi1!=null;fpi1=getItemFromClonalityFile(cf1, minLength, maxNcount)){
				if(!fpi1.getName1().equals(last)){
					last=fpi1.getName1();
					singlets1.write(last+"\n");
				}
			}
		}
		
		if(fpi2!=null){
			//print remaining as singlets
			System.err.println("printing the remenenants of file 2");
			last=fpi2.getName1();
			singlets2.write(last+"\n");
			if(nextfpi2==null){
				fpi2= getItemFromClonalityFile(cf2, minLength, maxNcount);
			}else{
				fpi2=new filteredPairs_item(nextfpi2);
			}
			for(;fpi2!=null;fpi2=getItemFromClonalityFile(cf2, minLength, maxNcount)){
				if(!fpi2.getName1().equals(last)){
					last=fpi2.getName1();
					singlets2.write(last+"\n");
				}
			}
		}
		singlets1.close();
		singlets2.close();
	}
	
	private static filteredPairs_item getItemFromClonalityFile(BufferedReader in,int minLength, int maxNcount)throws Exception{
		boolean done=false;
		filteredPairs_item fpi=null;
		while(!done){
			String line=in.readLine();
			if(line==null){
				done=true;
			}else{
				String[] l= line.split("\t");
				int length=Integer.parseInt(l[1]);
				if(length>=minLength){
					int nCount=Integer.parseInt(l[2]);
					if(nCount<=maxNcount){
						fpi=new filteredPairs_item(l[0], length, Integer.parseInt(l[3]));
						done=true;
					}
				}
			}
		}
		return fpi;
	}
	
	public static void analyzeClonality(ArrayList<String> fastqFiles, int minLength, int radix,String outfile, String tmpPrefix)throws Exception{
		final char[] alphabet = new char[]{'A','C','G','N','T'};
		//Generate buckets
		final ArrayList<String> buckets= new ArrayList<String>();
		buckets.add("");
		for(int i=0;i<radix;i++){
			while(buckets.get(0).length()==i){
				final String prefix=buckets.remove(0);
				for (char c : alphabet) {
					buckets.add(prefix+c);
				}
			}
		}
		final String first= buckets.remove(0);
		final HashMap<String, ObjectOutputStream> tmpOuts= new HashMap<String, ObjectOutputStream>();
		for (String bucket : buckets) {
			tmpOuts.put(bucket, new ObjectOutputStream(new FileOutputStream(tmpPrefix+"_"+bucket+".dat")));
		}
		clonalityChecker ccCur= new clonalityChecker(minLength);
		//parse the input file
		fastqParser fqp;
		FastqSeq fqs;
		String curPrefix;
		System.err.println("Reading the files...");
		for (String fastqFile : fastqFiles) {
			fqp= new fastqParser(new BufferedReader(new FileReader(fastqFile)),"");
//			fqp=new FastqParser_nio(fastqFile, fastqUtils.getPrefix(fastqFile));
			System.err.println(fastqFile);
			for(int i=0;fqp.hasNext();i++){
				if(i%10000==0){
					System.err.print("\t"+i+"\r");
				}
				fqs=fqp.next();
				curPrefix=fqp.getSeq().substring(0, radix);
				if(curPrefix.equals(first)){
					ccCur.add(fqs);
				}else{
					tmpOuts.get(curPrefix).writeObject(fqs);
				}
			}
		}
		for (ObjectOutputStream oosl : tmpOuts.values()) {
			oosl.flush();
		}
		for (ObjectOutputStream oosl : tmpOuts.values()) {
			oosl.close();
		}
		System.err.print("Analyzing and sorting... \n\tBucket 1\r");
		
		ccCur.postPooling();
		ArrayList<String> curStringList= new ArrayList<String>();
		int cluster=0;
		curStringList=ccCur.outList(cluster);
		cluster=ccCur.getEndCluster();
		ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream(tmpPrefix+"_"+first+".dat"));
		Collections.sort(curStringList);
		for (String line : curStringList) {
			oos.writeObject(line);
		}
		oos.flush();
		oos.close();
		ObjectInputStream ois;
		int count=2;
		for (String bucket : buckets) {
			System.err.print("\tBucket "+(count++)+": ("+bucket+")\n");
			ois= new ObjectInputStream(new FileInputStream(tmpPrefix+"_"+bucket+".dat"));
			ccCur= new clonalityChecker(minLength);
			try{
				fqs=(FastqSeq)(ois.readObject());
			}catch(EOFException eofe){
				fqs=null;
			}
			for(;fqs!=null;){
				ccCur.add(fqs);
				try{
					fqs=(FastqSeq)(ois.readObject());
				}catch(EOFException eofe){
					fqs=null;
				}
			}
			ois.close();
			ccCur.postPooling();
			curStringList= ccCur.outList(cluster);
			cluster=ccCur.getEndCluster();
			Collections.sort(curStringList);
			oos= new ObjectOutputStream(new FileOutputStream(tmpPrefix+"_"+bucket+".dat"));
			for (String line : curStringList) {
				oos.writeObject(line);
			}
			oos.flush();
			oos.close();
		}
		System.err.println("Merging temporary files...");
		BufferedWriter out= new BufferedWriter(new FileWriter(outfile));
		ObjectInputStream[] oiss= new ObjectInputStream[1+buckets.size()];
		oiss[0]=new ObjectInputStream(new FileInputStream(tmpPrefix+"_"+first+".dat"));
		for(int i=1;i<oiss.length;i++){
			oiss[i]=new ObjectInputStream(new FileInputStream(tmpPrefix+"_"+buckets.get(i-1)+".dat"));
		}
		//populate and initialize
		ArrayList<analyzeClonality_sortTuple> firstLines= new ArrayList<analyzeClonality_sortTuple>();
		String line;
		analyzeClonality_sortTuple tuple;
		for(int i=0;i<oiss.length;i++){
			try{
				line= (String) (oiss[i].readObject());
			}catch(EOFException eofe){
				line=null;
			}
			if(line!=null){
				firstLines.add(new analyzeClonality_sortTuple(i, line));
			}
		}
		Collections.sort(firstLines);
//		System.err.println(firstLines.size());
		while(firstLines.size()>0){
			tuple=firstLines.remove(0);
//			System.err.println(tuple.reader);
			out.write(tuple.line+"\n");
			try{
				line=(String) (oiss[tuple.reader].readObject());
			}catch(EOFException eofe){
				line=null;
			}
			if(firstLines.size()>0){
//				System.err.println(line);
//				System.err.println(firstLines.get(0).line);
//				System.err.println(line.compareTo(firstLines.get(0).line));
//				System.err.println("a".compareTo("b"));
				while(line!=null && line.compareTo(firstLines.get(0).line)<0){
					out.write(line+"\n");
					try{
						line=(String) (oiss[tuple.reader].readObject());
					}catch(EOFException eofe){
						line=null;
					}
				}
				if(line==null){
//					System.err.println("Closing reader: "+tuple.reader+", "+firstLines.size()+" files left.");
					oiss[tuple.reader].close();
				}else{
					tuple.line=line;
					firstLines.add(tuple);
					Collections.sort(firstLines);
				}
			}else{
				//flush the last file
				while(line!=null){
					out.write(line+"\n");
					try{
						line=(String) (oiss[tuple.reader].readObject());
					}catch(EOFException eofe){
						line=null;
					}
				}
				oiss[tuple.reader].close();
			}
		}
		out.close();
	}
	
//	public static void analyzeClonality(String fastqFile,int minLength,String outfile)throws Exception{
//		clonalityChecker cc= new clonalityChecker(minLength);
//		fastqParser fqp= new fastqParser(new BufferedReader(new FileReader(fastqFile)));
//		
//		System.err.println("Reading data...");
//		for(int i=1;fqp.hasNext();i++){
//			if(i%1000000==0){
//				System.err.println(i);
//			}
//			cc.add(fqp.next());
//		}
//		System.err.println("Pooling reads...");
//		cc.postPooling();
//		System.err.println("Printin results...");
//		BufferedWriter out= new BufferedWriter(new FileWriter(outfile));
//		cc.print(out);
//		out.close();
//		System.err.println("Done!");
//	}
	
	
	public static void newblerHeader(String fastqFile, String library, String outprefix)throws Exception{
		fastqParser fqp= new fastqParser(new BufferedReader(new FileReader(fastqFile)),"");
		FastqSeq fqs;
		String header;
		BufferedWriter fasta= new BufferedWriter(new FileWriter(outprefix+".fa"));
		BufferedWriter qual= new BufferedWriter(new FileWriter(outprefix+".qual"));
		boolean initialize=true;
		String tail="";
		for(;fqp.hasNext();){
			fqs= fqp.next();
			if(initialize){
				final char end=fqs.getQname().charAt(fqs.getQname().length()-1);
				tail=" dir=";
				if(end=='1'){
					tail+="F";
				}else if (end=='2'){
					tail+="R";
				}else{
					throw new Exception("Unknown direction of read with header:\n"+fqs.getHeader());
				}
				tail+=" library="+library;
			}
			header="@"+fqs.getQname()+" template="+fqs.getQname().substring(0, fqs.getQname().length()-2)+tail;
			fqs.setHeader(header);
			fasta.write(fqs.toFastaSeq().toString()+"\n");
			qual.write(fqs.toPhredQualSeq().toString()+"\n");
		}
		fasta.close();
		qual.close();
	}
}

class analyzeClonality_sortTuple implements Comparable<analyzeClonality_sortTuple>{
	int reader;
	String line;
	
	
	
	public analyzeClonality_sortTuple(int reader, String line) {
		super();
		this.reader = reader;
		this.line = line;
	}



	public int compareTo(analyzeClonality_sortTuple arg0) {
		return this.line.compareTo(arg0.line);
	}
	
}

class filteredPairs_pair{
	private int id1,id2;
	
	filteredPairs_pair(int id1,int id2){
		this.id1=id1;
		this.id2=id2;
	}

	public int getId1() {
		return id1;
	}



	public int getId2() {
		return id2;
	}



	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id1;
		result = prime * result + id2;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		filteredPairs_pair other = (filteredPairs_pair) obj;
		if (id1 != other.id1)
			return false;
		if (id2 != other.id2)
			return false;
		return true;
	}
}

class filteredPairs_item implements Comparable<filteredPairs_item>{
	private String name1,pair,name2;
	private int length1,length2,totalLength;
	private HashSet<Integer> clusters1;
	private boolean singlet;
	
	filteredPairs_item(String[] l) {
		this(l[0], Integer.parseInt(l[1]), Integer.parseInt(l[3]));
	}
	
	filteredPairs_item(String name,int length, int cluster1){
		this.setName1(name);
		this.setLength1(length);
		this.clusters1= new HashSet<Integer>();
		this.addCluster1(cluster1);
		this.singlet=true;
	}
	
	filteredPairs_item(String name1,String name2, int length1, int length2, int cluster) {
		this(name1,length1,cluster);
		this.setLength2(length2);
		this.setName2(name2);
	}
	
	filteredPairs_item(filteredPairs_item fpi){
		this.name1=fpi.name1;
		this.name2=fpi.name2;
		this.pair=fpi.pair;
		this.length1=fpi.length1;
		this.length2=fpi.length2;
		this.totalLength=fpi.totalLength;
		this.singlet=fpi.singlet;
		this.clusters1= new HashSet<Integer>();
		this.clusters1.addAll(fpi.clusters1);
	}
	
	public int getOneCluster(){
		int cluster=-1;
		for (Integer i : clusters1) {
			cluster=i;
			break;
		}
		return cluster;
	}
	
	public HashSet<Integer> getClusters1() {
		return clusters1;
	}

	private void updatePair(){
		if(name1.length()>2){
			pair=name1.substring(0,name1.length()-2);
		}else{
			pair="";
		}
	}
	
	void addCluster1(int cluster1){
		clusters1.add(cluster1);
	}
	
	
	public boolean getSinglet(){
		return singlet;
	}

	public String getName1() {
		return name1;
	}

	public void setName1(String name1) {
		this.name1 = name1;
		this.updatePair();
	}

	public int getLength1() {
		return length1;
	}

	public void setLength1(int length1) {
		this.length1 = length1;
		totalLength=this.length1+this.length2;
	}
	
	public String getName2() {
		return name2;
	}

	public void setName2(String name2) {
		this.name2 = name2;
		this.addMate();
	}

	public int getLength2() {
		return length2;
	}
	
	public int getTotalLength(){
		return totalLength;
	}

	public void setLength2(int length2) {
		this.length2 = length2;
		totalLength=this.length1+this.length2;
	}
	public String getPair() {
		return pair;
	}

	public void addMate(){
		this.singlet=false;
	}

	public int compareTo(filteredPairs_item o) {
		return this.getTotalLength()-o.getTotalLength();
	}
	
}
